home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / StringContent.java < prev    next >
Text File  |  1998-06-30  |  15KB  |  487 lines

  1. /*
  2.  * @(#)StringContent.java    1.33 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.util.Vector;
  23. import java.io.Serializable;
  24. import com.sun.java.swing.undo.*;
  25. import com.sun.java.swing.SwingUtilities;
  26.  
  27. /**
  28.  * An implementation of the AbstractDocument.Content interface that is 
  29.  * a brute force implementation that is useful for relatively small 
  30.  * documents and/or debugging.  It manages the character content
  31.  * as a simple character array.  It is also quite inefficient.  
  32.  * <p>
  33.  * It is generally recommended that the gap buffer or piece table 
  34.  * implementations be used instead.  This buffer does not scale up
  35.  * to large sizes.
  36.  * <p>
  37.  * Warning: serialized objects of this class will not be compatible with
  38.  * future swing releases.  The current serialization support is appropriate 
  39.  * for short term storage or RMI between Swing1.0 applications.  It will
  40.  * not be possible to load serialized Swing1.0 objects with future releases
  41.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  42.  * baseline for the serialized form of Swing objects.
  43.  *
  44.  * @author  Timothy Prinzing
  45.  * @version 1.33 04/09/98
  46.  */
  47. public final class StringContent implements AbstractDocument.Content, Serializable {
  48.  
  49.     /**
  50.      * Creates a new StringContent object.  Initial size defaults to 10.
  51.      */
  52.     public StringContent() {
  53.     this(10);
  54.     }
  55.  
  56.     /**
  57.      * Creates a new StringContent object, with the initial
  58.      * size specified.  If the length is < 1, a size of 1 is used.
  59.      *
  60.      * @param initialLength the initial size
  61.      */
  62.     public StringContent(int initialLength) {
  63.     if (initialLength < 1) {
  64.         initialLength = 1;
  65.     }
  66.     data = new char[initialLength];
  67.     data[0] = '\n';
  68.     count = 1;
  69.     }
  70.  
  71.     /**
  72.      * Returns the length of the content.
  73.      *
  74.      * @return the length >= 1
  75.      * @see AbstractDocument.Content#length
  76.      */
  77.     public int length() {
  78.     return count;
  79.     }
  80.  
  81.     /**
  82.      * Inserts a string into the content.
  83.      *
  84.      * @param where the starting position >= 0 && < length()
  85.      * @param str the non-null string to insert
  86.      * @return an UndoableEdit object for undoing
  87.      * @exception BadLocationException if the specified position is invalid
  88.      * @see AbstractDocument.Content#insertString
  89.      */
  90.     public UndoableEdit insertString(int where, String str) throws BadLocationException {
  91.     if (where >= count) {
  92.         throw new BadLocationException("Invalid location", count);
  93.     }
  94.     char[] chars = str.toCharArray();
  95.     replace(where, 0, chars, 0, chars.length);
  96.     if (marks != null) {
  97.         updateMarksForInsert(where, str.length());
  98.     }
  99.     return new InsertUndo(where, str.length());
  100.     }
  101.  
  102.     /**
  103.      * Removes part of the content.  where + nitems must be < length().
  104.      *
  105.      * @param where the starting position >= 0
  106.      * @param nitems the number of characters to remove >= 0
  107.      * @return an UndoableEdit object for undoing
  108.      * @exception BadLocationException if the specified position is invalid
  109.      * @see AbstractDocument.Content#remove
  110.      */
  111.     public UndoableEdit remove(int where, int nitems) throws BadLocationException {
  112.     if (where + nitems >= count) {
  113.         throw new BadLocationException("Invalid range", count);
  114.     }
  115.     String removedString = getString(where, nitems);
  116.     UndoableEdit edit = new RemoveUndo(where, removedString);
  117.     replace(where, nitems, empty, 0, 0);
  118.     if (marks != null) {
  119.         updateMarksForRemove(where, nitems);
  120.     }
  121.     return edit;
  122.     
  123.     }
  124.  
  125.     /**
  126.      * Retrieves a portion of the content.  where + len must be <= length().
  127.      *
  128.      * @param where the starting position >= 0
  129.      * @param len the length to retrieve >= 0
  130.      * @return a string representing the content; may be empty
  131.      * @exception BadLocationException if the specified position is invalid
  132.      * @see AbstractDocument.Content#getString
  133.      */
  134.     public String getString(int where, int len) throws BadLocationException {
  135.     if (where + len > count) {
  136.         throw new BadLocationException("Invalid range", count);
  137.     }
  138.     return new String(data, where, len);
  139.     }
  140.  
  141.     /**
  142.      * Retrieves a portion of the content.  where + len must be <= length()
  143.      *
  144.      * @param where the starting position >= 0
  145.      * @param len the number of characters to retrieve >= 0
  146.      * @param chars the Segment object to return the characters in
  147.      * @exception BadLocationException if the specified position is invalid
  148.      * @see AbstractDocument.Content#getChars
  149.      */
  150.     public void getChars(int where, int len, Segment chars) throws BadLocationException {
  151.     if (where + len > count) {
  152.         throw new BadLocationException("Invalid location", count);
  153.     }
  154.     chars.array = data;
  155.     chars.offset = where;
  156.     chars.count = len;
  157.     }
  158.  
  159.     /**
  160.      * Creates a position within the content that will
  161.      * track change as the content is mutated.
  162.      *
  163.      * @param offset the offset to create a position for >= 0
  164.      * @return the position
  165.      * @exception BadLocationException if the specified position is invalid
  166.      */
  167.     public Position createPosition(int offset) throws BadLocationException {
  168.     // some small documents won't have any sticky positions
  169.     // at all, so the buffer is created lazily.
  170.     if (marks == null) {
  171.         marks = new Vector();
  172.     }
  173.     return new StickyPosition(offset);
  174.     }
  175.  
  176.     // --- local methods ---------------------------------------
  177.  
  178.     /**
  179.      * Replaces some of the characters in the array
  180.      * @param offset  offset into the array to start the replace
  181.      * @param length  number of characters to remove
  182.      * @param replArray replacement array
  183.      * @param replOffset offset into the replacement array
  184.      * @param replLength number of character to use from the
  185.      *   replacement array.
  186.      */
  187.     void replace(int offset, int length, 
  188.          char[] replArray, int replOffset, int replLength) {
  189.     int delta = replLength - length;
  190.     int src = offset + length;
  191.     int nmove = count - src;
  192.     int dest = src + delta;
  193.     if ((count + delta) >= data.length) {
  194.         // need to grow the array
  195.         int newLength = Math.max(2*data.length, count + delta);
  196.         char[] newData = new char[newLength];
  197.         System.arraycopy(data, 0, newData, 0, offset);
  198.         System.arraycopy(replArray, replOffset, newData, offset, replLength);
  199.         System.arraycopy(data, src, newData, dest, nmove);
  200.         data = newData;
  201.     } else {
  202.         // patch the existing array
  203.         System.arraycopy(data, src, data, dest, nmove);
  204.         System.arraycopy(replArray, replOffset, data, offset, replLength);
  205.     }
  206.     count = count + delta;
  207.     }
  208.  
  209.     void resize(int ncount) {
  210.     char[] ndata = new char[ncount];
  211.     System.arraycopy(data, 0, ndata, 0, Math.min(ncount, count));
  212.     data = ndata;
  213.     }
  214.  
  215.     synchronized void updateMarksForInsert(int offset, int length) {
  216.     if (offset == 0) {
  217.         // zero is a special case where we update only
  218.         // marks after it.
  219.         offset = 1;
  220.     }
  221.     int n = marks.size();
  222.     for (int i = 0; i < n; i++) {
  223.         PosRec mark = (PosRec) marks.elementAt(i);
  224.         if (mark.unused) {
  225.         // this record is no longer used, get rid of it
  226.         marks.removeElementAt(i);
  227.         i -= 1;
  228.         n -= 1;
  229.         } else if (mark.offset >= offset) {
  230.         mark.offset += length;
  231.         }
  232.     }
  233.     }
  234.  
  235.     synchronized void updateMarksForRemove(int offset, int length) {
  236.     int n = marks.size();
  237.     for (int i = 0; i < n; i++) {
  238.         PosRec mark = (PosRec) marks.elementAt(i);
  239.         if (mark.unused) {
  240.         // this record is no longer used, get rid of it
  241.         marks.removeElementAt(i);
  242.         i -= 1;
  243.         n -= 1;
  244.         } else if (mark.offset >= (offset + length)) {
  245.         mark.offset -= length;
  246.         } else if (mark.offset >= offset) {
  247.         mark.offset = offset;
  248.         }
  249.     }
  250.     }
  251.  
  252.     /**
  253.      * Returns a Vector containing instances of UndoPosRef for the
  254.      * Positions in the range
  255.      * <code>offset</code> to <code>offset</code> + <code>length</code>.
  256.      * If <code>v</code> is not null the matching Positions are placed in
  257.      * there. The vector with the resulting Positions are returned.
  258.      *
  259.      * @param v the Vector to use, with a new one created on null
  260.      * @param offset the starting offset >= 0
  261.      * @param length the length >= 0
  262.      * @return the set of instances
  263.      */
  264.     protected Vector getPositionsInRange(Vector v, int offset,
  265.                               int length) {
  266.     int n = marks.size();
  267.     int end = offset + length;
  268.     Vector placeIn = (v == null) ? new Vector() : v;
  269.     for (int i = 0; i < n; i++) {
  270.         PosRec mark = (PosRec) marks.elementAt(i);
  271.         if (mark.unused) {
  272.         // this record is no longer used, get rid of it
  273.         marks.removeElementAt(i);
  274.         i -= 1;
  275.         n -= 1;
  276.         } else if(mark.offset >= offset && mark.offset <= end)
  277.         placeIn.addElement(new UndoPosRef(mark));
  278.     }
  279.     return placeIn;
  280.     }
  281.  
  282.     /**
  283.      * Resets the location for all the UndoPosRef instances
  284.      * in <code>positions</code>.
  285.      *
  286.      * @param positions the positions of the instances
  287.      */
  288.     protected void updateUndoPositions(Vector positions) {
  289.     for(int counter = positions.size() - 1; counter >= 0; counter--) {
  290.         UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
  291.         // Check if the Position is still valid.
  292.         if(ref.rec.unused) {
  293.         positions.removeElementAt(counter);
  294.         }
  295.         else
  296.         ref.resetLocation();
  297.     }
  298.     }
  299.  
  300.     private static final char[] empty = new char[0];
  301.     private char[] data;
  302.     private int count;
  303.     transient Vector marks;
  304.  
  305.     /**
  306.      * holds the data for a mark... seperately from
  307.      * the real mark so that the real mark can be 
  308.      * collected if there are no more references to
  309.      * it.... the update table holds only a reference
  310.      * to this grungey thing.
  311.      */
  312.     final class PosRec {
  313.  
  314.     PosRec(int offset) {
  315.         this.offset = offset;
  316.     }
  317.  
  318.     int offset;
  319.     boolean unused;
  320.     }
  321.  
  322.     /**
  323.      * This really wants to be a weak reference but
  324.      * in 1.1 we don't have a 100% pure solution for
  325.      * this... so this class trys to hack a solution 
  326.      * to causing the marks to be collected.
  327.      */
  328.     final class StickyPosition implements Position {
  329.  
  330.     StickyPosition(int offset) {
  331.         rec = new PosRec(offset);
  332.         marks.addElement(rec);
  333.     }
  334.  
  335.         public int getOffset() {
  336.         return rec.offset;
  337.     }
  338.  
  339.     protected void finalize() throws Throwable {
  340.         // schedule the record to be removed later
  341.         // on another thread.
  342.         rec.unused = true;
  343.     }
  344.  
  345.         public String toString() {
  346.         return Integer.toString(getOffset());
  347.     }
  348.  
  349.     PosRec rec;
  350.     }
  351.  
  352.     /**
  353.      * Used to hold a reference to a Position that is being reset as the
  354.      * result of removing from the content.
  355.      */
  356.     final class UndoPosRef {
  357.     UndoPosRef(PosRec rec) {
  358.         this.rec = rec;
  359.         this.undoLocation = rec.offset;
  360.     }
  361.  
  362.     /**
  363.      * Resets the location of the Position to the offset when the
  364.      * receiver was instantiated.
  365.      */
  366.     protected void resetLocation() {
  367.         rec.offset = undoLocation;
  368.     }
  369.  
  370.     /** Location to reset to when resetLocatino is invoked. */
  371.     protected int undoLocation;
  372.     /** Position to reset offset. */
  373.     protected PosRec rec;
  374.     }
  375.  
  376.     /**
  377.      * UnoableEdit created for inserts.
  378.      */
  379.     class InsertUndo extends AbstractUndoableEdit {
  380.     protected InsertUndo(int offset, int length) {
  381.         super();
  382.         this.offset = offset;
  383.         this.length = length;
  384.     }
  385.  
  386.     public void undo() throws CannotUndoException {
  387.         super.undo();
  388.         try {
  389.         synchronized(StringContent.this) {
  390.             // Get the Positions in the range being removed.
  391.             if(marks != null)
  392.             posRefs = getPositionsInRange(null, offset, length);
  393.             string = getString(offset, length);
  394.             remove(offset, length);
  395.         }
  396.         } catch (BadLocationException bl) {
  397.           throw new CannotUndoException();
  398.         }
  399.     }
  400.  
  401.     public void redo() throws CannotRedoException {
  402.         super.redo();
  403.         try {
  404.         synchronized(StringContent.this) {
  405.             insertString(offset, string);
  406.             string = null;
  407.             // Update the Positions that were in the range removed.
  408.             if(posRefs != null) {
  409.             updateUndoPositions(posRefs);
  410.             posRefs = null;
  411.             }
  412.           }
  413.         } catch (BadLocationException bl) {
  414.           throw new CannotRedoException();
  415.         }
  416.     }
  417.  
  418.     // Where the string goes.
  419.     protected int offset;
  420.     // Length of the string.
  421.     protected int length;
  422.     // The string that was inserted. To cut down on space needed this
  423.     // will only be valid after an undo.
  424.     protected String string;
  425.     // An array of instances of UndoPosRef for the Positions in the
  426.     // range that was removed, valid after undo.
  427.     protected Vector posRefs;
  428.     }
  429.  
  430.  
  431.     /**
  432.      * UndoableEdit created for removes.
  433.      */
  434.     class RemoveUndo extends AbstractUndoableEdit {
  435.     protected RemoveUndo(int offset, String string) {
  436.         super();
  437.         this.offset = offset;
  438.         this.string = string;
  439.         this.length = string.length();
  440.         if(marks != null)
  441.         posRefs = getPositionsInRange(null, offset, length);
  442.     }
  443.  
  444.     public void undo() throws CannotUndoException {
  445.         super.undo();
  446.         try {
  447.         synchronized(StringContent.this) {
  448.             insertString(offset, string);
  449.             // Update the Positions that were in the range removed.
  450.             if(posRefs != null) {
  451.             updateUndoPositions(posRefs);
  452.             posRefs = null;
  453.             }
  454.             string = null;
  455.         }
  456.         } catch (BadLocationException bl) {
  457.           throw new CannotUndoException();
  458.         }
  459.     }
  460.  
  461.     public void redo() throws CannotRedoException {
  462.         super.redo();
  463.         try {
  464.         synchronized(StringContent.this) {
  465.             string = getString(offset, length);
  466.             // Get the Positions in the range being removed.
  467.             if(marks != null)
  468.             posRefs = getPositionsInRange(null, offset, length);
  469.             remove(offset, length);
  470.         }
  471.         } catch (BadLocationException bl) {
  472.           throw new CannotRedoException();
  473.         }
  474.     }
  475.  
  476.     // Where the string goes.
  477.     protected int offset;
  478.     // Length of the string.
  479.     protected int length;
  480.     // The string that was inserted. This will be null after an undo.
  481.     protected String string;
  482.     // An array of instances of UndoPosRef for the Positions in the
  483.     // range that was removed, valid before undo.
  484.     protected Vector posRefs;
  485.     }
  486. }
  487.